package pl.msikora.android.lapmagic.view.overlay;

import java.util.ArrayList;

import pl.msikora.android.lapmagic.R;
import pl.msikora.android.lapmagic.database.LapMagicDatabaseHelper;
import pl.msikora.android.lapmagic.database.PointsTable;
import pl.msikora.android.lapmagic.view.TrackDetailActivity;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.Toast;

import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;

// TODO edit point radius (maybe no need to)

/**
 * Class for managing points of a track - their creation, edition, moving.
 * 
 * @author Maciej Sikora
 * 
 */
public class TrackItemizedOverlay extends ItemizedOverlay<PointOverlayItem> {
	
	private final String TAG = "TrackItemizedOverlay";
	private ArrayList<PointOverlayItem> overlays = 
			new ArrayList<PointOverlayItem>();
	private Drawable marker = null;
	private PointOverlayItem inDrag = null;
	private PointOverlayItem tappedItem = null;
	private ImageView dragImage = null;
	private int xDragImageOffset = 0;
	private int yDragImageOffset = 0;
	private int xDragTouchOffset = 0;
	private int yDragTouchOffset = 0;
	private boolean dragMode = false;
	private Context context;
	
	protected Object actionMode = null;
	
	// don't use it out of the class
	private TrackItemizedOverlay(Drawable defaultMarker, ImageView dragImage) {
		super(boundCenterBottom(defaultMarker));
		this.marker = defaultMarker;
		this.dragImage = dragImage;		
		xDragImageOffset = dragImage.getDrawable().getIntrinsicWidth() / 2;
		yDragImageOffset = dragImage.getDrawable().getIntrinsicHeight();
		
		populate(); // add this due to a known bug - NullPointerException
	}
	
	public TrackItemizedOverlay(Drawable defaultMarker, ImageView dragImage, 
			Context context) {
		this(defaultMarker, dragImage);
		this.context = context;
	}
	
	public void addOverlay(PointOverlayItem overlay) {
		overlays.add(overlay);
		populate();
	}

	@Override
	protected PointOverlayItem createItem(int i) {
		return overlays.get(i);
	}

	@Override
	public int size() {
		return overlays.size();
	}
	
	public ArrayList<PointOverlayItem> getOverlays() {
		return overlays;
	}
	
	public boolean isDragModeEnabled() {
		return dragMode;
	}
	
	public void enableDragMode() {
		dragMode = true;
	}
	
	public void disableDragMode() {
		dragMode = false;
	}
		
	@Override
	protected boolean onTap(int index) {
		if (actionMode != null)
			return false;
		
		tappedItem = overlays.get(index);
		Log.d(TAG, "tapped point " + tappedItem.getName() + " of type " 
				+ tappedItem.getType() + " with radius "
				+ tappedItem.getRadius());
		actionMode = ((TrackDetailActivity) context)
				.startActionMode(actionModeCallback);
		return true;
	}
	
    @Override
    public boolean onTouchEvent(MotionEvent event, MapView mapView) {
	    final int action = event.getAction();
	    final int x = (int) event.getX();
	    final int y = (int) event.getY();
	    boolean result = false;
	    
	    if (!dragMode) // return and handle a tap
	    	return result;
	
		if (action == MotionEvent.ACTION_DOWN) {
			for (PointOverlayItem item : overlays) {
			    Point p = new Point(0,0);
			    mapView.getProjection().toPixels(item.getPoint(), p);
			
			    if (hitTest(item, marker, x - p.x, y - p.y)) {
				    result = true;
				    inDrag = item;
				    overlays.remove(inDrag);
				    populate();
				
				    xDragTouchOffset = 0;
				    yDragTouchOffset = 0;
				    
				    setDragImagePosition(p.x, p.y);
				    dragImage.setVisibility(View.VISIBLE);
				    
				    xDragTouchOffset = x - p.x;
				    yDragTouchOffset = y - p.y;				    
				    
				    break;
			    }
			}
		}
		else if (action == MotionEvent.ACTION_MOVE && inDrag != null) {
			setDragImagePosition(x, y);
			result = true;
		}
		else if (action == MotionEvent.ACTION_UP && inDrag != null) {
			dragImage.setVisibility(View.GONE);
			
			GeoPoint pt = mapView.getProjection()
					.fromPixels(x - xDragTouchOffset, y - yDragTouchOffset);
			
			PointOverlayItem toDrop = 
					new PointOverlayItem(pt, inDrag.getName(), 
							inDrag.getType(), inDrag.getId(), 
							inDrag.getRadius());
			toDrop.setChanged();
			overlays.add(toDrop);
			populate();

			inDrag=null;
			result=true;
		}
	
		return (result || super.onTouchEvent(event, mapView));
    }

    private void setDragImagePosition(int x, int y) {
    	RelativeLayout.LayoutParams lp = 
    			(RelativeLayout.LayoutParams) dragImage.getLayoutParams();
    	
    	lp.setMargins(x - xDragImageOffset-xDragTouchOffset,
                y - yDragImageOffset-yDragTouchOffset, 0, 0);
    	dragImage.setLayoutParams(lp);
    }
    
    private ActionMode.Callback actionModeCallback = 
    		new ActionMode.Callback() {
    	
    	private View pointNameActionView = null;
    	private View pointTypeActionView = null;
    	private Spinner pointTypeSpinner = null;
    	ArrayAdapter<CharSequence> adapter = null;
    	private boolean deleteMode = false;
    	private String startType;
    	private String splitType;
				
    	@Override
		public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            
    		startType = TrackItemizedOverlay.this.context
    				.getString(R.string.track_detail_point_type_start);
    		splitType = TrackItemizedOverlay.this.context
    				.getString(R.string.track_detail_point_type_split);

    		// set the appropriate point name at contextual menu bar
    		pointNameActionView = (View) menu.findItem(R.id.pointName)
    				.getActionView();
            EditText name = (EditText) pointNameActionView
            		.findViewById(R.id.pointEditText);
            name.setText(tappedItem.getName());
            
            // set the appropriate point type at contextual menu bar
            pointTypeActionView = (View) menu.findItem(R.id.pointType)
            		.getActionView();
            pointTypeSpinner = (Spinner) pointTypeActionView
            		.findViewById(R.id.pointEditType);
            adapter = ArrayAdapter.createFromResource(context,
            		R.array.pointTypes, android.R.layout.simple_spinner_item);
            adapter.setDropDownViewResource(
            		android.R.layout.simple_spinner_dropdown_item);
            pointTypeSpinner.setAdapter(adapter);
            pointTypeSpinner.setSelection(adapter.getPosition(tappedItem
            		.getType()));

            // prevent from defining more than one start point
            pointTypeSpinner.setOnItemSelectedListener(
            		new OnItemSelectedListener() {

				@Override
				public void onItemSelected(AdapterView<?> parent, View view,
						int position, long id) {

					// when set type to start
					if (parent.getItemAtPosition(position).toString()
							.equals(startType)) {
						for (PointOverlayItem item : overlays) {
							if (item != tappedItem && item.getType()
									.equals(startType)) {
								Toast.makeText(TrackItemizedOverlay.this
									.context, R.string.
									track_detail_avoid_many_start_points,
									Toast.LENGTH_LONG).show();
								pointTypeSpinner.setSelection((adapter
					            		.getPosition(splitType)));
								break;
							}
						}
					}
				}

				@Override
				public void onNothingSelected(AdapterView<?> parent) {
				}
			});

			return true;
		}
		
    	@Override
		public void onDestroyActionMode(ActionMode mode) {
    		if (deleteMode) {
    			if (tappedItem.getId() != -1) {
    				Log.d(TAG, "deleting point " + tappedItem.getName()
    						+ " from database");
    				deletePoint(tappedItem.getId());
    			}
    			
				Log.d(TAG, "deleting point " + tappedItem.getName()
						+ " from overlay");
				overlays.remove(tappedItem);
				setLastFocusedIndex(-1); // add this due to a known bug - ArrayIndexOutOfBounds
				populate();
				deleteMode = false;
    		}
    		else {
				EditText name = (EditText) pointNameActionView
						.findViewById(R.id.pointEditText);
				Log.d(TAG, "setting point name " + name.getText().toString());
				tappedItem.setName(name.getText().toString());
				
				String type = pointTypeSpinner.getSelectedItem().toString();
				Log.d(TAG, "setting point type " + type);
				tappedItem.setType(type);
    		}
			tappedItem = null;
			actionMode = null;
		}
		
		@Override
		public boolean onCreateActionMode(ActionMode mode, Menu menu) {
			MenuInflater inflater = mode.getMenuInflater();
			inflater.inflate(R.menu.track_detail_split_contextual, 
					menu);
			return true;
		}
		
		@Override
		public boolean onActionItemClicked(final ActionMode mode, 
				MenuItem item) {
			switch (item.getItemId()) {
			case R.id.deletePoint:
	    		AlertDialog.Builder builder = new AlertDialog.Builder(
	    			TrackItemizedOverlay.this.context);
	    		builder.setTitle(R.string.alert_dialog_delete_title)
	    			.setMessage(R.string.alert_dialog_delete_point)
	    			.setPositiveButton(R.string.alert_dialog_yes,
	    					new OnClickListener() {
						
						@Override
						public void onClick(DialogInterface dialog, int which) {
				    		Log.d(TAG, "switching delete mode ON");
				    		deleteMode = true;
				    		mode.finish();
						}
					})
					.setNegativeButton(R.string.alert_dialog_cancel,
							new OnClickListener() {
						
						@Override
						public void onClick(DialogInterface dialog, int which) {
							dialog.cancel();
						}
					});
	    		builder.create().show();
			default:
				return false;
			}
		}
		
		private void deletePoint(long id) {
			SQLiteDatabase db = LapMagicDatabaseHelper.getInstance(context)
					.getWritableDatabase();
			db.delete(PointsTable.TABLE_POINTS, "_id = " + id, null);
		}
		
	};
}
